# This Python file uses the following encoding: utf-8
import os, sys
import time
import re

from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QFileDialog
import matplotlib.pyplot as plt
import numpy as np
import datetime
from astropy.io import fits
from scipy.stats import trim_mean, sigmaclip
# from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas

""" ==========================================================================================
# On my Ubuntu22.04 machine beast, the following imports MUST be commented out !!!
# from PySide6.QtCharts import QChartView, QChart, QLineSeries, QValueAxis, QScatterSeries
========================================================================================== """

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)

from ccd.ccd_config import ccdDataConfig, ccdDataFilter, ccdPreOverScanCorrection, estimateGainUsingCorrectedFlats

from ccd_thread import (
    WorkerThread_compareFlats,
    WorkerThread_processPTC
)

# Important:
# You need to run the following command to generate the ui_form.py file
#     pyside6-uic form.ui -o ui_form.py, or
#     pyside2-uic form.ui -o ui_form.py
from ui_form import Ui_Widget
from matplotlibwidget import MatplotlibWidget


# class Widget(QWidget):
#     def __init__(self, parent=None):
#         super().__init__(parent)
#         self.ui = Ui_Widget()
#         self.ui.setupUi(self)


class CCDquick(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_Widget()
        self.ui.setupUi(self)

        # ###########################################################
        # global variables
        self.wkdir          = ''        # work directory

        self.flat1_fname    = None      # filename of flat1
        self.flat2_fname    = None      # filename of flat2
        self.bias1_fname    = None      # filename of bias1
        self.bias2_fname    = None      # filename of bias2

        self.do_gain        = False     # if True, estiamte gain of 16 OS; if False: gain will not be estimated
        self.do_row_os_corr = False     # if True, do row overscan correction (84 rows); if False, no correction
        self.do_col_os_corr = False     # if True, do col overscan correction (71 cols); if False, no correction
        self.do_row_corr_1st= False     # if True and self.do_row_os_corr == True and self.do_col_os_corr == True,
                                        #   row correction is performed first

        self.flat1ExpTime   = 1.0
        self.flat2ExpTime   = 1.0

        # self.showCorrecteFlats  = False
        # self.showCorrecteBias   = False
        # ###########################################################


        # ###########################################################
        # Connect buttons to slots
        self.ui.btn_choose_flat1.clicked.connect(self.slot_choose_flat1)
        self.ui.btn_choose_flat2.clicked.connect(self.slot_choose_flat2)
        self.ui.btn_choose_bias1.clicked.connect(self.slot_choose_bias1)
        self.ui.btn_choose_bias2.clicked.connect(self.slot_choose_bias2)

        self.ui.btn_clearFlats.clicked.connect(self.slot_clearFlats)
        self.ui.btn_clearBias.clicked.connect(self.slot_clearBias)

        self.ui.btn_wkdir.clicked.connect(self.slot_choose_workdir)

        # self.ui.btn_compare_corrected_flats.clicked.connect(self.slot_compare_corrected_flats)
        self.ui.btn_compare_corrected_flats.clicked.connect(self.slot_qthread_compare_flats)

        self.ui.btn_fileList.clicked.connect(self.slot_processPTC_parseFileList)

        self.ui.btn_processPTC.clicked.connect(self.slot_processPTC)
        self.ui.btn_clearLogPTC.clicked.connect(self.slot_processPTC_log_clear)

        # 退出按钮
        self.ui.btn_exit.clicked.connect(self.slot_btn_exit_clicked)

        # compute button
        self.ui.btn_compute.clicked.connect(self.slot_compute)

        # Log clear
        self.ui.btn_clearLog.clicked.connect(self.slot_clearLog)

        # 画图按钮
        self.ui.btn_testPlots.clicked.connect(self.slot_plot_test_clicked)

        # 图画清除按钮
        self.ui.btn_clearPlots.clicked.connect(self.slot_plot_clear_clicked)

        # 创建Matplotlib画布
        self.canvas = MatplotlibWidget(self.ui.mplWidget)

        # 测试画图功能

        # ###########################################################
        # 将画布添加到布局
        layout = QVBoxLayout(self.ui.mplWidget)
        layout.addWidget(self.canvas)


        self.ui.btn_compare_corrected_flats.setStyleSheet('background-color: chartreuse')
        self.ui.btn_choose_flat1.setEnabled(False)
        self.ui.btn_choose_flat2.setEnabled(False)
        self.ui.btn_choose_bias1.setEnabled(False)
        self.ui.btn_choose_bias2.setEnabled(False)
        self.ui.btn_compare_corrected_flats.setEnabled(False)


        self.ui.btn_estimateGain.clicked.connect(self.slot_estimate_gain)

        # ###########################################################


        # ###########################################################
        # 创建子线程列表，用于后台执行大量计算任务
        # self._threadPool = None
        # self.threads = []
        # self.threads = None
        # self.thread = None
        
        self.thread_compare_flat = None
        self.thread_process_ptc = None

        # ###########################################################

    def getCurrentDateTime(self):
        """
        Return current date and time.
        """
        dt = '- [{}] '.format( datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] )
        return dt

    def write_log(self, log_msg):
        """
        Write normal log message
        """
        self.ui.plainTextEdit_log.appendPlainText(self.getCurrentDateTime() + log_msg)

    def write_warning(self, log_msg):
        """
        Write warning log message
        """
        self.ui.plainTextEdit_log.appendPlainText(self.getCurrentDateTime() +'* WARNING *: '+log_msg)

    def slot_clearLog(self):
        """
        Clear log.
        """
        self.ui.plainTextEdit_log.clear()


    def slot_choose_flat1(self):
        """
        Open QFileDiaglog to select a flat.
        """
        self.flat1_fname = None

        if os.path.isdir( self.wkdir ):
            self.flat1_fname = QFileDialog.getOpenFileName(self, 'OpenFile', self.wkdir, '*.fits;;(*)')[0]
        else:
            self.flat1_fname = QFileDialog.getOpenFileName(self, 'OpenFile', '.', '*.fits;;(*)')[0]

        if self.flat1_fname:
            self.write_log('chosen: {}'.format(self.flat1_fname))
            self.ui.lineEdit_flat1.setText(self.flat1_fname)
            print('type(self.flat1_fname): {}'.format(type(self.flat1_fname)))
        else:
            self.write_warning('the chosen file dose not exist')


    def slot_choose_flat2(self):
        """
        Open QFileDiaglog to select a flat.
        """
        self.flat2_fname = None

        if os.path.isdir( self.wkdir ):
            self.flat2_fname = QFileDialog.getOpenFileName(self, 'OpenFile', self.wkdir, '*.fits;;(*)')[0]
        else:
            self.flat2_fname = QFileDialog.getOpenFileName(self, 'OpenFile', '.', '*.fits;;(*)')[0]

        if self.flat2_fname:
            self.write_log('chosen: {}'.format(self.flat2_fname))
            self.ui.lineEdit_flat2.setText(self.flat2_fname)
        else:
            self.write_warning('the chosen file dose not exist')


    def slot_choose_bias1(self):
        """
        Open QFileDiaglog to select a flat.
        """
        self.bias1_fname = None

        if os.path.isdir( self.wkdir ):
            self.bias1_fname = QFileDialog.getOpenFileName(self, 'OpenFile', self.wkdir, '*.fits;;(*)')[0]
        else:
            self.bias1_fname = QFileDialog.getOpenFileName(self, 'OpenFile', '.', '*.fits;;(*)')[0]

        if self.bias1_fname:
            self.write_log('chosen: {}'.format(self.bias1_fname))
            self.ui.lineEdit_bias1.setText(self.bias1_fname)
        else:
            self.write_warning('the chosen file dose not exist')


    def slot_choose_bias2(self):
        """
        Open QFileDiaglog to select a flat.
        """
        self.bias2_fname = None

        if os.path.isdir( self.wkdir ):
            self.bias2_fname = QFileDialog.getOpenFileName(self, 'OpenFile', self.wkdir, '*.fits;;(*)')[0]
        else:
            self.bias2_fname = QFileDialog.getOpenFileName(self, 'OpenFile', '.', '*.fits;;(*)')[0]

        if self.bias2_fname:
            self.write_log('chosen: {}'.format(self.bias2_fname))
            self.ui.lineEdit_bias2.setText(self.bias2_fname)
        else:
            self.write_warning('the chosen file dose not exist')

    def slot_choose_workdir(self):
        self.wkdir = None
        self.wkdir = QFileDialog.getExistingDirectory(self, 'Choose Dir','', QFileDialog.ShowDirsOnly)
        if self.wkdir:
            self.write_log('chosen {} as work directory'.format(self.wkdir))
            self.ui.lineEdit_wkdir.setText(self.wkdir)
            self.ui.btn_choose_flat1.setEnabled(True)
            self.ui.btn_choose_flat2.setEnabled(True)
            self.ui.btn_choose_bias1.setEnabled(True)
            self.ui.btn_choose_bias2.setEnabled(True)
            self.ui.btn_compare_corrected_flats.setEnabled(True)
        else:
            self.write_warning('the chosen work directory dose not exist')

    def slot_compute(self):
        self.write_log('call slot_compute')
        if self.flat1_fname is None or self.flat2_fname is None:
            self.write_warning('either self.flat1_fname or self.flat2_fname is None, check !!!')
        pass


    def slot_estimate_gain(self):
        print('debug: estimate gain ..')
        self.write_log('start estimating gain using row-over/col-pre scan corrected flats')
        print('start estimating gain using row-over/col-pre scan corrected flats')

        flat1 = None
        flat2 = None
        bias1 = None
        bias2 = None
        fro_gain1 = None
        fro_gain2 = None
        bro_gain1 = None
        bro_gain2 = None

        self.flat1_fname = self.ui.lineEdit_flat1.text()

        corr_flat1 = '{}/corrected_flat1.fits'.format(self.wkdir)
        corr_flat2 = '{}/corrected_flat2.fits'.format(self.wkdir)
        corr_bias1 = '{}/corrected_bias1.fits'.format(self.wkdir)
        corr_bias2 = '{}/corrected_bias2.fits'.format(self.wkdir)

        if os.path.isfile(self.flat1_fname):
            flat1, fro_gain1 = ccdPreOverScanCorrection(self.flat1_fname, applyGainCorrection=self.ui.checkBox_applyGainCorrection.isChecked())
            if self.ui.checkBox_showCorrectedFlats.isChecked():
                hdu = fits.PrimaryHDU(flat1)
                hdu.writeto(corr_flat1,overwrite=True)
                self.write_log('saved {}'.format(corr_flat1))
            self.write_log('got row-over/col-pre scan corrected flat from: {}'.format(self.flat1_fname))
        else:
            self.write_warning("flat1: '{}' does not exist".format(self.flat1_fname))

        if self.flat2_fname is not None:
            flat2, fro_gain2 = ccdPreOverScanCorrection(self.flat2_fname, applyGainCorrection=self.ui.checkBox_applyGainCorrection.isChecked())
            if self.ui.checkBox_showCorrectedFlats.isChecked():
                hdu = fits.PrimaryHDU(flat2)
                hdu.writeto(corr_flat2,overwrite=True)
                self.write_log('saved {}'.format(corr_flat2))
            self.write_log('got row-over/col-pre scan corrected flat from: {}'.format(self.flat2_fname))
        else:
            self.write_warning("flat2: '{}' does not exist".format(self.flat2_fname))

        if self.bias1_fname is not None:
            bias1, bro_gain1 = ccdPreOverScanCorrection(self.bias1_fname, applyGainCorrection=self.ui.checkBox_applyGainCorrection.isChecked())
            if self.ui.checkBox_showCorrectedBias.isChecked():
                hdu = fits.PrimaryHDU(bias1)
                hdu.writeto(corr_bias1,overwrite=True)
                self.write_log('saved {}'.format(corr_bias1))
            self.write_log('got row-over/col-pre scan corrected flat from: {}'.format(self.bias1_fname))
        else:
            self.write_warning("bias1: '{}' does not exist".format(self.bias1_fname))

        if self.bias2_fname is not None:
            bias2, bro_gain2 = ccdPreOverScanCorrection(self.bias2_fname, applyGainCorrection=self.ui.checkBox_applyGainCorrection.isChecked())
            if self.ui.checkBox_showCorrectedBias.isChecked():
                hdu = fits.PrimaryHDU(bias2)
                hdu.writeto(corr_bias2,overwrite=True)
                self.write_log('saved {}'.format(corr_bias2))
            self.write_log('got row-over/col-pre scan corrected flat from: {}'.format(self.bias2_fname))
        else:
            self.write_warning("bias2: '{}' does not exist".format(self.bias2_fname))

        if self.ui.checkBox_showCorrectedFlats.isChecked():
            if self.flat1_fname is not None:
                fname1 = corr_flat1
            else:
                fname1 = ''

            if self.flat2_fname is not None:
                fname2 = corr_flat2
            else:
                fname2 = ''
            cmd = f'ds9 {fname1} {fname2} &'
            os.system(cmd)

        if self.ui.checkBox_showCorrectedBias.isChecked():
            if self.bias1_fname is not None:
                fname1 = corr_bias1
            else:
                fname1 = ''

            if self.bias2_fname is not None:
                fname2 = corr_bias2
            else:
                fname2 = ''
            self.write_log(f'opening {fname1} {fname2}')
            cmd = f'ds9 {fname1} {fname2} &'
            os.system(cmd)

        flat_mean = None
        flat_var  = None
        gain      = None

        # sigmaClip = False
        sigmaClip = True

        if self.ui.checkBox_sigmaClip.isChecked():
            sigmaClip = True

        if flat1 is not None and flat2 is not None:
            if bias1 is not None and bias2 is not None:
                flat_mean, flat_var, gain = estimateGainUsingCorrectedFlats((flat1-bias1),(flat2-bias2), sClip=sigmaClip)
            else:
                flat_mean, flat_var, gain = estimateGainUsingCorrectedFlats(flat1,flat2, sClip=sigmaClip)

        if gain is not None:
            for i in range(16):
                self.write_log('OS-{:02} gain: {:6.4f}'.format(i+1, gain[i]))
            ch = np.arange(16)
            self.canvas.plot_gain(ch, gain)

    def slot_clearFlats(self):
        self.flat1_fname = None
        self.flat2_fname = None
        self.ui.lineEdit_flat1.setText('')
        self.ui.lineEdit_flat2.setText('')
        self.write_log('clear selected flats')

    def slot_clearBias(self):
        self.bias1_fname = None
        self.bias2_fname = None
        self.ui.lineEdit_bias1.setText('')
        self.ui.lineEdit_bias2.setText('')
        self.write_log('clear selected bias')

    def slot_plot_test_clicked(self):
        self.canvas.plot_test()
        # self.canvas.plot()

    def slot_plot_clear_clicked(self):
        self.canvas.clear()

    def slot_btn_exit_clicked(self):
        # print("**** exit ****")
        self.close()


    def slot_update_log(self,msg):
        """
        仅用于测试多线程编程
        """
        self.write_log(msg)

    def slot_qthread_compare_flats(self):
        """
        测试点击UI的按钮之后，通过slot创建一个新的线程，然后新线程在后台继续执行，而按钮所连接的slot不被阻塞、继续后续执行
        """

        # 检查是否有子线程正在运行
        # if self.thread_compare_flat:
        #     if self.thread_compare_flat.isRunning():
        #         thread.cancel()
        #         thread.wait()

        # self.threads = []

        self.thread_compare_flat = None

        thread = WorkerThread_compareFlats(
                    thread_id = 1,
                    wkdir = self.wkdir,
                    flatFilename1 = self.flat1_fname,
                    flatFilename2 = self.flat2_fname,
                    biasFilename1 = self.bias1_fname,
                    biasFilename2 = self.bias2_fname,
                    applyGainCorrection = self.ui.checkBox_applyGainCorrection.isChecked(),
                    applySelfNorm = self.ui.checkBox_applySelfNorm.isChecked(),
                    showCorrectedFlats = self.ui.checkBox_showCorrectedFlats.isChecked(),
                    showCorrectedBias = self.ui.checkBox_showCorrectedBias.isChecked(),
                    anchorToZero = self.ui.checkBox_anchorToZero.isChecked(),
                    correctExpTime = self.ui.checkBox_correctExpTime.isChecked(),
                    expTime1 = float(self.ui.lineEdit_flat1ExpTime.text()),
                    expTime2 = float(self.ui.lineEdit_flat2ExpTime.text()),
                    plotFlatRatio = self.ui.checkBox_plotFlatRatio.isChecked(),
                    useColPreScan= self.ui.checkBox_useColPreScan.isChecked()
                    )

        thread.signal_message.connect(self.slot_qthread_compare_flats_message)
        thread.signal_result.connect(self.slot_qthread_compare_flats_result)
        thread.signal_plot_fro_gain.connect(self.slot_plot_fro_gain)
        thread.signal_finished.connect(self.slot_qthread_compare_flats_finished)

        self.thread_compare_flat = thread
        # self.threads.append(thread)
        thread.start()
        self.write_log(f'thread 1 is running')

        # 将按钮disable，直到线程里的计算工作完成并emit finished信号
        self.ui.btn_compare_corrected_flats.setEnabled(False)
        self.ui.btn_compare_corrected_flats.setStyleSheet('background-color: orangered')
        self.write_log('start comparing row-over/col-pre scan corrected flats')

    def slot_plot_fro_gain(self, result):
        """
        与线程WorkerThread_compareFlats的信号signal_plot_fro_gain相关联
        """
        fro_gain1 = result["fro_gain1"]
        fro_gain2 = result["fro_gain2"]

        #调用self.canvas.plot_row_overscan_gain
        self.canvas.plot_row_overscan_gain(fro_gain1, fro_gain2)

    def slot_qthread_compare_flats_message(self, message):
        self.write_log(message)

    def slot_qthread_compare_flats_result(self, result):
        print(result)
        if result['corr_flat1'] is not None and result['corr_flat2'] is not None:
            cmd = f"ds9 {result['corr_flat1']} {result['corr_flat2']} &"
            self.write_log(cmd)
            os.system(cmd)

        if result['corr_bias1'] is not None and result['corr_bias2'] is not None:
            cmd = f"ds9 {result['corr_bias1']} {result['corr_bias2']} &"
            b1 = fits.getdata(result['corr_bias1'])
            b2 = fits.getdata(result['corr_bias2'])
            db = b1-b2
            db_rms = []
            cdc = ccdDataConfig()
            for i in range(16):
                idx_row1, idx_row2, idx_col1, idx_col2 = cdc.getImgChanRegion(ch=i)
                d = sigmaclip( db[idx_row1:idx_row2, idx_col1:idx_col2].flatten(), 3, 3)[0]
                # sigmaclip(fmean[idx_row1:idx_row2, idx_col1:idx_col2].flatten(), 4, 4)[0]
                db_rms.append( ( np.var(d)/2 )**0.5 )

            print(db_rms)
            self.write_log(cmd)
            os.system(cmd)

        if result['ratio18'] is not None or result['ratio916'] is not None:
            ylim = [float(self.ui.lineEdit_yMin.text()), float(self.ui.lineEdit_yMax.text())]
            self.canvas.plot_flat_ratio(ratio18=result['ratio18'], ratio916=result['ratio916'], ylim=ylim)

        if self.ui.checkBox_showFlatRatioFits.isChecked() and result['flat_ratio_ffname'] is not None:
            cmd = f"ds9 {result['flat_ratio_ffname']}&"
            self.write_log(cmd)
            os.system(cmd)

        # if self.ui.checkBox_showCorrectedBias.isChecked():
        #     cmd = f"ds9 {result['flat_ratio_ffname']}&"
        #     self.write(cmd)
        #     os.system(cmd)

    def slot_qthread_compare_flats_finished(self):
        self.ui.btn_compare_corrected_flats.setEnabled(True)
        self.ui.btn_compare_corrected_flats.setStyleSheet('background-color: chartreuse')

        if self.thread_compare_flat.isRunning():
            self.write_log('self.thread_compare_flat.isRunning() is TRUE')
        else:
            self.thread_compare_flat = None
            self.write_log('reset self.thread_compare_flat to None')


    # ##########################################################################################
    # PTC数据处理

    def slot_processPTC_log(self, msg):
        self.ui.plainTextEdit_logPTC.appendPlainText(self.getCurrentDateTime() + msg)

    def slot_processPTC_log_clear(self):
        self.ui.plainTextEdit_logPTC.clear()

    def slot_processPTC_result(self, result):
        """
        接受子线程返回的result，再调用slot_processPTC_plotgain(result)画图
        """
        print(result['flux'])
        print(result['var'])
        print(result['expTime'])
        print(result['gain'])

        if self.ui.checkBox_plotGain.isChecked():
            gainResult = {}
            for ch in range(16):
                gainResult[f"gain_os_{ch+1}"] = result['gain'][ch]

            self.slot_processPTC_plotgain(gainResult)
        else:
            _flux, _var = {}, {}
            for ch in range(16):
                _flux[f"flux_os_{ch+1}"] = result['flux'][ch]
                _var[f"var_os_{ch+1}"] = result['var'][ch]

            _texp = result['expTime']
            print(f"_texp: {_texp}")
            self.slot_processPTC_plot_PTC_PRC(flux=_flux, var=_var, texp=_texp)

    def slot_processPTC_plotgain(self, gainResult):
        self.canvas.plot_ptc_gain(gainResult)

    def slot_processPTC_plot_PTC_PRC(self, flux, var, texp, savcFig=False ):
        self.canvas.plot_ptc_prc_curve(flux, var, texp, plotPRC=self.ui.checkBox_plotPRC.isChecked())

    def slot_processPTC_finished(self):
        self.ui.btn_processPTC.setEnabled(True)
        self.ui.btn_processPTC.setStyleSheet('background-color: chartreuse')

        if self.thread_process_ptc.isRunning():
            self.slot_processPTC_log('self.thread_process_ptc.isRunning() is TRUE')
        else:
            self.thread_process_ptc = None
            self.slot_processPTC_log('reset self.thread_process_ptc to None')

    def slot_processPTC_parseFileList(self):
        """
        整理好ptc数据处理所需的本底、ptc平场数据文件名、ptc平场曝光时间
        """
        fileList = None
        if os.path.isdir( self.wkdir ):
            fileList = QFileDialog.getOpenFileName(self, 'OpenFile', self.wkdir, '*.txt')[0]
        else:
            fileList = QFileDialog.getOpenFileName(self, 'OpenFile', '.', '*.txt')[0]

        ccd_idx = self.ui.lineEdit_ccdIdx.text()
        if ccd_idx != '00' and ccd_idx != '01' and ccd_idx != '02' and ccd_idx != '03':
            self.slot_processPTC_log(f"ccd_idx is {ccd_idx}, MUST be one of 00,01,02,03!")

        blist, plist1, plist2, tlist = [], [], [], []
        if os.path.isfile(fileList):
            fp = open(fileList, 'r')
            lines = fp.readlines()
            for line in lines:
                if line.startswith('bias'):
                    bfname = "{}/{}_{}.fits".format(self.wkdir, line.strip('\n'), ccd_idx)
                    blist.append(bfname)
                    msg = "blist added: {}".format(bfname)
                    self.slot_processPTC_log(msg)
                elif line.startswith('ptc') or line.startswith('flat'):
                    tmp = [x for x in line.strip('\n').split(" ") if x != ""]
                    if tmp[2] == '1':
                        # plist1.append(f"{self.wkdir}/{tmp[0]}_{ccd_idx}.fits")
                        # self.slot_processPTC_log(f"plist1 added: {self.wkdir}/{tmp[0]}_{ccd_idx}.fits")
                        plist1.append(f"{self.wkdir}/{tmp[0]}.fits")
                        self.slot_processPTC_log(f"plist1 added: {self.wkdir}/{tmp[0]}.fits")
                        tlist.append(float(tmp[1]))
                        self.slot_processPTC_log(f"tlist added: {float(tmp[1])}")
                    elif tmp[2] == '2':
                        # plist2.append(f"{self.wkdir}/{tmp[0]}_{ccd_idx}.fits")
                        # self.slot_processPTC_log(f"plist2 added: {self.wkdir}/{tmp[0]}_{ccd_idx}.fits")
                        plist2.append(f"{self.wkdir}/{tmp[0]}.fits")
                        self.slot_processPTC_log(f"plist2 added: {self.wkdir}/{tmp[0]}.fits")

            fp.close()
            return blist, plist1, plist2, tlist
        else:
            self.slot_processPTC_log(f"{fileList} does not exits.")
            return None, None, None, None

    def slot_processPTC(self):

        blist, plist1, plist2, tlist = self.slot_processPTC_parseFileList()
        print(f"tlist: {tlist}")
        # return

        if blist is None:
            self.slot_processPTC_log(f"blist is None")

        if plist1 is None:
            self.slot_processPTC_log(f"plist1 is None")
            return
        
        if plist2 is None:
            self.slot_processPTC_log(f"plist2 is None")
            return
        
        if len(plist1) != len(plist2):
            self.slot_processPTC_log(f"len(plist1) != len(plist2)")
            return

        if tlist is None:
            self.slot_processPTC_log(f"tlist is None")
            return

        if len(plist1) != len(tlist):
            self.slot_processPTC_log(f"len(plist) != len(tlist)")
            self.slot_processPTC_log(f"{plist1}")
            self.slot_processPTC_log(f"{tlist}")
            return

        self.thread_process_ptc = None

        thread = WorkerThread_processPTC(
                    ccd_idx = self.ui.lineEdit_ccdIdx.text(),
                    outdir = self.ui.lineEdit_outdir.text(),
                    blist = blist,
                    plist1 = plist1,
                    plist2 = plist2,
                    tlist = tlist,
                    subtractBias = self.ui.checkBox_subtractBias.isChecked(),
                    applyFlatCorrection = self.ui.checkBox_applyFlatCorrection.isChecked(),
                    sigmaClip = self.ui.checkBox_sigmaClip.isChecked(),
                    exportPTCFigs = self.ui.checkBox_exportPTCFigs,
                    exportPRCFigs = self.ui.checkBox_exportPRCFigs,
                    plotGain = self.ui.checkBox_plotGain.isChecked(),
                    useColPreScan= self.ui.checkBox_useColPreScan.isChecked()
            )

        thread.signal_message.connect(self.slot_processPTC_log)
        thread.signal_result.connect(self.slot_processPTC_result)
        thread.signal_plot_gain.connect(self.slot_processPTC_result)
        thread.signal_finished.connect(self.slot_processPTC_finished)

        self.thread_process_ptc = thread

        thread.start()
        self.slot_processPTC_log(f"self.thread_process_ptc starts")

        self.ui.btn_processPTC.setEnabled(False)
        self.ui.btn_processPTC.setStyleSheet('background-color: orangered')


    # ##########################################################################################




# if __name__ == "__main__":
#     app = QApplication(sys.argv)
#     widget = Widget()
#     widget.show()
#     sys.exit(app.exec())
